package com.sd.repay.controller;

import com.sd.repay.annotation.MerAuthStatusCheck;
import com.sd.repay.channel.cj.CJBusinessImpl;
import com.sd.repay.common.CommonResult;
import com.sd.repay.common.Constants;
import com.sd.repay.jedis.impl.JedisClientSingle;
import com.sd.repay.pojo.PlanConfig;
import com.sd.repay.pojo.RepayPlan;
import com.sd.repay.pojo.RepayPlanDetail;
import com.sd.repay.pojo.ZqMerchant;
import com.sd.repay.service.*;
import com.sd.repay.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by sd on 2018/08/21
 * 还款计划控制类
 */
@Controller
@RequestMapping(value = "/merRepayMgr")
public class RepayPlanController {

    private static final Logger log = LoggerFactory.getLogger(RepayPlanController.class);

    @Autowired
    private MerchantService merchantService;
    @Autowired
    private RepayPlanService repayPlanService;
    @Autowired
    private RepayPlanDetailService planDetailService;
    @Autowired
    private CardManageService cardManageService;
    @Autowired
    private PayChannelService payChannelService;
    @Autowired
    private MerFeeService merFeeService;
    @Autowired
    private JedisClientSingle jedisClientSingle;
    @Autowired
    private ZqMerchantService zqMerchantService;


    /**
     * 建立计划下发计划参数
     *
     * @param params
     * @return
     */
    @MerAuthStatusCheck
    @RequestMapping(value = "/loadPlanData", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
    @ResponseBody
    public CommonResult loadPlanData(@RequestParam Map<String, String> params) {

        String merchantNo = WebUtil.getCurrMerchantUser().getMerchantNo();
        log.info("商户号[{}]点击还款下发计划参数接口[{}]", new Object[]{merchantNo, JsonUtil.objectToJson(params)});

        String repayType = params.get("repayType");
        String cardNo = params.get("cardNo");
        try {

            if (StringUtil.isEmpty(repayType, cardNo)) {
                return CommonResult.build(403, "必要参数不能有空值");
            }
            //校验商户信息
            Map<String, Object> merInfoMap = merchantService.getMerInfoByMerNo(merchantNo);
            if (merInfoMap == null || merInfoMap.isEmpty()) {
                return CommonResult.build(403, "商户不存在");
            }
            String oneAgentNo = StringUtil.filterNull(merInfoMap.get("one_agent_no"));

            //校验卡片信息
            Map<String, Object> cardInfo = cardManageService.getCardManageByCardNo(cardNo);
            if (cardInfo == null || cardInfo.isEmpty()) {
                return CommonResult.build(403, "卡信息不存在");
            }
            String settleBankNo = String.valueOf(cardInfo.get("settle_bank_no"));
            String idCardNo = String.valueOf(cardInfo.get("id_card_no"));

            PlanConfig planConfig = WebUtil.getPlanConfigByType(repayType);
            if (planConfig == null) {
                return CommonResult.build(403, "还款类型配置有误");
            }

            //路由通道，存入redis缓存
            String acqCode = payChannelService.routePayChannel(settleBankNo, repayType);
            if (StringUtils.isBlank(acqCode)) {
                return CommonResult.build(403, "暂不支持此卡建立计划，请稍候再试");
            }

            //如果路由到畅捷，先进行商户进件操作
            if (Constants.PAY_ACQ_CODE_CJ.equals(acqCode)) {
                //获取进件商户号
                ZqMerchant zqMerchant = zqMerchantService.getByMerAndAcq(merchantNo, Constants.PAY_ACQ_CODE_CJ);
                log.info("商户号[{}]畅捷进件数据库保存信息[{}]", new Object[]{merchantNo, JsonUtil.objectToJson(zqMerchant)});
                if (null == zqMerchant || StringUtils.isEmpty(zqMerchant.getZqMerchantNo()) || !Constants.ZQ_MER_STATUS_SUCC.equals(zqMerchant.getZqStatus())) {

                    //获取商户实名认证的储蓄卡信息
                    Map<String, Object> settleCardMap = cardManageService.getSettleCardInfoByIdCard(idCardNo);
                    log.info("商户号[{}]实名认证储蓄卡信息[{}]", new Object[]{merchantNo, JsonUtil.objectToJson(settleCardMap)});
                    if (settleCardMap == null || settleCardMap.isEmpty()) {
                        return CommonResult.build(403, "商户实名认证信息有误，请稍候再试");
                    }
                    String mobileNo = String.valueOf(settleCardMap.get("mobile_no"));
                    String accountNo = String.valueOf(settleCardMap.get("account_no"));
                    String accountName = String.valueOf(settleCardMap.get("account_name"));

                    Map<String, Object> posCardInfo = cardManageService.getPosCardInfoByAccountNo(accountNo);
                    log.info("商户号[{}]储蓄卡号[{}]系统卡bin表对应信息[{}]", new Object[]{merchantNo, accountNo, JsonUtil.objectToJson(posCardInfo)});
                    String posBankName = String.valueOf(posCardInfo.get("bank_name"));
                    String posBankNo = String.valueOf(posCardInfo.get("bank_no"));

                    Map<String, String> cjMerRegParam = new HashMap<>();
                    cjMerRegParam.put("merchantNo", merchantNo);
                    cjMerRegParam.put("cardNo", cardNo);
                    cjMerRegParam.put("idCardNo", idCardNo);
                    cjMerRegParam.put("mobileNo", mobileNo);
                    cjMerRegParam.put("bankAccountNo", accountNo);
                    cjMerRegParam.put("bankAccountName", accountName);
                    cjMerRegParam.put("bankName", posBankName);
                    cjMerRegParam.put("bankNo", posBankNo);

                    try {
                        CommonResult cjMerRegRes = CJBusinessImpl.merReg(cjMerRegParam);
                        if (cjMerRegRes.getStatus() != 200) {
                            return cjMerRegRes;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.info("商户号[{}]畅捷进件异常[{}]", new Object[]{merchantNo, e});
                        return CommonResult.build(403, "商户进件失败，请稍候再试");
                    }
                }
            }

            String origRouteCacheNo = "ORIG-ROUTE_CACHE_NO" + merchantNo + cardNo + acqCode + System.currentTimeMillis();
            jedisClientSingle.set(origRouteCacheNo, acqCode, 300);

            Map<String, String> dataMap = new HashMap<>();
            dataMap.put("origRouteCacheNo", origRouteCacheNo);
            dataMap.put("repayMinAmount", String.valueOf(planConfig.getRepayMinAmount()));
            dataMap.put("repayMaxAmount", String.valueOf(planConfig.getRepayMaxAmount()));
            return CommonResult.ok(dataMap);

        } catch (Exception e) {
            e.printStackTrace();
            log.error("商户号[{}]下发计划配置信息异常[{}]", new Object[]{merchantNo, e});
            return CommonResult.build(403, "操作失败，请稍候再试");
        }
    }


    /**
     * 预览还款计划
     *
     * @param params
     * @return
     */
    @MerAuthStatusCheck
    @RequestMapping(value = "/previewRepayPlan", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
    @ResponseBody
    public CommonResult previewRepayPlan(@RequestParam Map<String, String> params) {

        String merchantNo = WebUtil.getCurrMerchantUser().getMerchantNo();
        log.info("商户号[{}]预览计划参数[{}]", new Object[]{merchantNo, JsonUtil.objectToJson(params)});

        String oemType = params.get("oemType");
        String repayType = params.get("repayType");
        String cardNo = params.get("cardNo");
        String repayAmount = params.get("repayAmount");
        String origRouteCacheNo = params.get("origRouteCacheNo");

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        oemType = StringUtil.isBlank(oemType) ? Constants.DEFAULT_OEM_TYPE : oemType;
        try {
            if (StringUtil.isEmpty(repayType, cardNo, repayAmount, origRouteCacheNo)) {
                return CommonResult.build(403, "必要参数不能有空值");
            }
            String acqCode = jedisClientSingle.get(origRouteCacheNo);
            if (StringUtils.isBlank(acqCode)) {
                return CommonResult.build(403, "参数不合法");
            }
            //校验商户信息
            Map<String, Object> merInfoMap = merchantService.getMerInfoByMerNo(merchantNo);
            if (merInfoMap == null || merInfoMap.isEmpty()) {
                return CommonResult.build(403, "商户不存在");
            }
            String oneAgentNo = StringUtil.filterNull(merInfoMap.get("one_agent_no"));

            //校验卡片信息
            Map<String, Object> cardInfo = cardManageService.getCardManageByCardNo(cardNo);
            if (cardInfo == null || cardInfo.isEmpty()) {
                return CommonResult.build(403, "卡信息不存在");
            }
            String idCardNo = StringUtil.filterNull(cardInfo.get("id_card_no"));
            String mobile = StringUtil.filterNull(cardInfo.get("mobile_no"));
            String settleBankNo = StringUtil.filterNull(cardInfo.get("bank_no"));
            String accountName = StringUtil.filterNull(cardInfo.get("account_name"));
            String accountNo = StringUtil.filterNull(cardInfo.get("account_no"));
            String statementDateStr = String.valueOf(cardInfo.get("statement_date"));
            String repaymentDateStr = String.valueOf(cardInfo.get("repayment_date"));

            CommonResult repayTimeRes = getCreditRepayTime(statementDateStr, repaymentDateStr);
            if (repayTimeRes.getStatus() != 200) {
                return repayTimeRes;
            }
            Map<String, String> repayTimeData = (Map<String, String>) repayTimeRes.getData();
            String repayBeginTime = repayTimeData.get("repayBeginTime");
            String repayEndTime = repayTimeData.get("repayEndTime");
            //预留2天还款时间
            repayEndTime = DateUtil.format(DateUtil.addDays(DateUtil.parseDate(repayEndTime, "yyyy-MM-dd"), -2), "yyyy-MM-dd");
            if (DateUtil.daysBetween(repayBeginTime, repayEndTime) < 0) {
                return CommonResult.build(403, "只能在账单日和还款日前2天内建立计划");
            }
            //校验还款日期
            int repayDays = DateUtil.daysBetween(repayBeginTime, repayEndTime);

            Map<String, Object> routeChannel = WebUtil.getPayChannelInfo(acqCode);
            String acqMerchantNo = StringUtil.filterNull(routeChannel.get("pay_merchant_no"));
            String channelStatus = StringUtil.filterNull(routeChannel.get("channel_status"));
            String acqSingleFee = StringUtil.filterNull(routeChannel.get("withdraw_single_fee"));

            if (!"1".equals(channelStatus)) {
                log.info(acqCode + "当前通道已关闭，不能建立计划");
                return CommonResult.build(403, "暂不支持此卡建立计划");
            }
            if (!payChannelService.isSupportBankByChannel(settleBankNo, acqCode)) {
                return CommonResult.build(403, "暂不支持此卡建立计划");
            }

            //获取商户费率信息
            Map<String, Object> merFeeMap = merFeeService.getMerFeeByMerNoAndOemType(Constants.MER_TYPE_MERCHANT, merchantNo, oemType);
            if (merFeeMap == null || merFeeMap.isEmpty()) {
                return CommonResult.build(403, "商户费率信息有误");
            }

            String merFeeRate = "", merSingleFee = "", repayDesc = "";
            //redis存放计划明细集合，生成计划时调用
            List<Map<String, String>> origRepayDetailListInfo = new ArrayList<>();

            int minRepayDays = 0;//最小还款天数
            int maxRepayDays = 0;//最大还款天数
            int totalRepayNum = 0;//总期数
            int dayRepayNum = 0;//每天还款次数
            int firstTradeAmount = 0;//首笔交易金额
            int totalMerFee = 0;//计算总手续费
            int totalPayAmount = 0;//总共消费金额

            //允许还款开始结束时间点
            PlanConfig planConfig = WebUtil.getPlanConfigByType(repayType);
            log.info("卡片编号：{}预览计划配置信息：{}", new Object[]{cardNo, JsonUtil.objectToJson(planConfig)});
            if (planConfig == null) {
                return CommonResult.build(403, "计划服务配置错误");
            }
            int repayAllowBeginHour = planConfig.getRepayAllowBeginHour();
            int repayAllowEndHour = planConfig.getRepayAllowEndHour();

            switch (repayType) {

                //快捷还款
                case Constants.REPAY_TYPE_QUICK: {

                    //获取商户快捷还款费率
                    merFeeRate = String.valueOf(merFeeMap.get("quick_fee_rate"));
                    merSingleFee = String.valueOf(merFeeMap.get("quick_single_fee"));

                    int dayMaxRepayAmount = CalRepaymentQuickPlan.quickDayMaxRepayNum * CalRepaymentQuickPlan.quickSingleMaxMoney;
                    int dayMinRepayAmount = CalRepaymentQuickPlan.quickDayMinRepayNum * CalRepaymentQuickPlan.quickSingleMinMoney;
                    int maxRepayAmount = dayMaxRepayAmount * repayDays;
                    BigDecimal nearPer = new BigDecimal(maxRepayAmount).divide(new BigDecimal(repayAmount), 2, BigDecimal.ROUND_UP);
                    if (nearPer.compareTo(new BigDecimal("0.8")) < 0) {
                        return CommonResult.build(403, "还款金额过大在当月可剩余的还款时间内无法完成，请核查再试");
                    }
                    Map<String, Integer> repayParamInfo = CalRepaymentQuickPlan.calDayRepayNumInfo(Integer.parseInt(repayAmount), repayDays, null);
                    //总期数
                    totalRepayNum = repayParamInfo.get("repayNum");
                    repayDays = repayParamInfo.get("repayDays");
                    dayRepayNum = repayParamInfo.get("dayRepayNum");
                    //计算总共消费金额
                    totalPayAmount = CalRepaymentQuickPlan.calTotalPayAmount(Integer.parseInt(repayAmount), totalRepayNum, Double.parseDouble(merFeeRate), Double.parseDouble(merSingleFee));
                    //获取首笔交易金额
                    firstTradeAmount = CalRepaymentQuickPlan.calFirstPayAmount(Integer.parseInt(repayAmount), totalRepayNum, repayDays, dayRepayNum);
                    //计算总手续费
                    totalMerFee = totalPayAmount - Integer.parseInt(repayAmount);

                    log.info("totalRepayNum:{},minRepayDays:{},repayDays:{},dayRepayNum:{},firstTradeAmount:{},totalPayAmount:{},totalMerFee:{}",
                            new Object[]{totalRepayNum, minRepayDays, repayDays, dayRepayNum, firstTradeAmount, totalPayAmount, totalMerFee});

                    //请求算法
                    origRepayDetailListInfo = CalRepaymentQuickPlan.repayPlanTask(totalPayAmount, firstTradeAmount, repayDays, dayRepayNum,
                            DateUtil.parseDate(repayBeginTime, "yyyy-MM-dd"), repayAllowBeginHour, repayAllowEndHour, totalRepayNum, new BigDecimal(merFeeRate), new BigDecimal(merSingleFee));

                    if (origRepayDetailListInfo == null || origRepayDetailListInfo.isEmpty()) {
                        return CommonResult.build(403, "计划预览异常，请修改还款金额、还款日期再试");
                    }

                    break;
                }
                case Constants.REPAY_TYPE_PERFECT: {
                    //获取商户智能还款费率
                    merFeeRate = String.valueOf(merFeeMap.get("perfect_fee_rate"));
                    merSingleFee = String.valueOf(merFeeMap.get("perfect_single_fee"));

                    int dayMaxRepayAmount = CalRepaymentPerfectPlan.perfectDayMaxRepayNum * CalRepaymentPerfectPlan.perfectSingleMaxMoney;
                    int dayMinRepayAmount = CalRepaymentPerfectPlan.perfectDayMinRepayNum * CalRepaymentPerfectPlan.perfectSingleMinMoney;
                    int maxRepayAmount = dayMaxRepayAmount * repayDays;
                    BigDecimal nearPer = new BigDecimal(maxRepayAmount).divide(new BigDecimal(repayAmount), 2, BigDecimal.ROUND_UP);
                    if (nearPer.compareTo(new BigDecimal("0.8")) < 0) {
                        return CommonResult.build(403, "还款金额过大在当月可剩余的还款时间内无法完成，请核查再试");
                    }
                    Map<String, Integer> repayParamInfo = CalRepaymentPerfectPlan.calDayRepayNumInfo(Integer.parseInt(repayAmount), repayDays, null);
                    //总期数
                    totalRepayNum = repayParamInfo.get("repayNum");
                    repayDays = repayParamInfo.get("repayDays");
                    dayRepayNum = repayParamInfo.get("dayRepayNum");
                    //计算总共消费金额
                    totalPayAmount = CalRepaymentPerfectPlan.calTotalPayAmount(Integer.parseInt(repayAmount), totalRepayNum, Double.parseDouble(merFeeRate), Double.parseDouble(merSingleFee));
                    //获取首笔交易金额
                    firstTradeAmount = CalRepaymentPerfectPlan.calFirstPayAmount(Integer.parseInt(repayAmount), totalRepayNum, repayDays, dayRepayNum);
                    //计算总手续费
                    totalMerFee = totalPayAmount - Integer.parseInt(repayAmount);

                    log.info("totalRepayNum:{},minRepayDays:{},repayDays:{},dayRepayNum:{},firstTradeAmount:{},totalPayAmount:{},totalMerFee:{}",
                            new Object[]{totalRepayNum, minRepayDays, repayDays, dayRepayNum, firstTradeAmount, totalPayAmount, totalMerFee});

                    //请求算法
                    origRepayDetailListInfo = CalRepaymentPerfectPlan.repayPlanTask(totalPayAmount, firstTradeAmount, repayDays, dayRepayNum,
                            DateUtil.parseDate(repayBeginTime, "yyyy-MM-dd"), repayAllowBeginHour, repayAllowEndHour, totalRepayNum, new BigDecimal(merFeeRate), new BigDecimal(merSingleFee));

                    if (origRepayDetailListInfo == null || origRepayDetailListInfo.isEmpty()) {
                        return CommonResult.build(403, "计划预览异常，请修改还款金额、还款日期再试");
                    }
                }
                default: {
                    return CommonResult.build(403, "还款类型不合法");
                }
            }

            //每天还款的详细数据
            List<Map<String, Object>> packRepayInfo = new ArrayList<>();

            List<String> dayList = new ArrayList<>();
            for (Map<String, String> planMap : origRepayDetailListInfo) {
                String planTime = planMap.get("planTime");
                String planTimeDay = sdf.format(sdf.parse(planTime));
                if (!dayList.contains(planTimeDay)) {
                    dayList.add(planTimeDay);
                }
            }
            int dayNum = 1;
            for (String planTimeDay : dayList) {
                Map<String, Object> packPlanMap = new HashMap<>();
                packPlanMap.put("planDay", "第" + dayNum + "天 " + planTimeDay);
                List<Map<String, String>> dayPlanList = new ArrayList<>();
                int dayPlanIndex = 1;
                int groupInIndex = 0;
                for (Map<String, String> planMap : origRepayDetailListInfo) {
                    String planType = planMap.get("planType");
                    String planTime = planMap.get("planTime");
                    String planTimeDayFormat = sdf.format(sdf.parse(planTime));
                    if (planTimeDay.equals(planTimeDayFormat)) {
                        String planAmount = planMap.get("planAmount");
                        if (groupInIndex == 0 && Constants.PLAN_DETAIL_TYPE_OUT.equals(planType)) {
                            //每一期里面的OUT
                            Map<String, String> packDayPlanMap = new HashMap<>();
                            packDayPlanMap.put("dayPlanIndex", String.valueOf((Integer.parseInt(planMap.get("planIndex")) + 1) / 2));
                            packDayPlanMap.put("planOutAmount", planAmount);
                            dayPlanList.add(packDayPlanMap);
                            groupInIndex++;
                        }
                        if (groupInIndex == 1 && Constants.PLAN_DETAIL_TYPE_IN.equals(planType)) {
                            //每一期里面的IN
                            dayPlanList.get(dayPlanList.size() - 1).put("planInAmount", planAmount);
                            groupInIndex++;
                        }
                        if (groupInIndex == 2) {
                            groupInIndex = 0;
                            dayPlanIndex++;
                        }
                    }
                }
                dayNum++;
                packPlanMap.put("dayPlanList", dayPlanList);
                packRepayInfo.add(packPlanMap);
            }
            repayDesc = repayDays + "天，总" + totalRepayNum + "期";

            //存入redis集合数据
            Map<String, Object> repayInfoRedisObj = new HashMap<>();
            repayInfoRedisObj.put("origRepayDetailListInfo", origRepayDetailListInfo);
            repayInfoRedisObj.put("acqCode", acqCode);
            repayInfoRedisObj.put("repayType", repayType);
            repayInfoRedisObj.put("cardNo", cardNo);
            repayInfoRedisObj.put("repayAmount", repayAmount);
            repayInfoRedisObj.put("firstTradeAmount", firstTradeAmount);
            repayInfoRedisObj.put("totalPayAmount", totalPayAmount);
            repayInfoRedisObj.put("totalMerFee", totalMerFee);
            repayInfoRedisObj.put("totalRepayNum", totalRepayNum);
            repayInfoRedisObj.put("repayBeginTime", repayBeginTime);
            repayInfoRedisObj.put("repayEndTime", repayEndTime);
            repayInfoRedisObj.put("repayDesc", repayDesc);
            repayInfoRedisObj.put("merFeeRate", merFeeRate);
            repayInfoRedisObj.put("merSingleFee", merSingleFee);

            String origRepaInfoCacheNo = "ORIG-REPAY-INFO_CACHE_KEY" + merchantNo + cardNo + System.currentTimeMillis();
            jedisClientSingle.setMap(origRepaInfoCacheNo, repayInfoRedisObj, 300);

            //返回前端数据
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("repayBeginTime", repayBeginTime);
            dataMap.put("repayEndTime", repayEndTime);
            dataMap.put("totalPayAmount", String.valueOf(totalPayAmount));
            dataMap.put("totalMerFee", String.valueOf(totalMerFee));
            dataMap.put("cardMinAmount", String.valueOf(firstTradeAmount));
            dataMap.put("repayDesc", repayDesc);
            dataMap.put("packRepayInfo", packRepayInfo);
            dataMap.put("origRepaInfoCacheNo", origRepaInfoCacheNo);
            return CommonResult.ok(dataMap);

        } catch (Exception e) {
            e.printStackTrace();
            log.error("商户号[{}]预览还款计划异常[{}]", new Object[]{merchantNo, e});
            return CommonResult.build(403, "操作失败，请稍候再试");
        }
    }

    /**
     * 生成还款计划
     *
     * @param request
     * @param params
     * @return
     */
    @MerAuthStatusCheck
    @RequestMapping(value = "/generateMerRepayPlan", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
    @ResponseBody
    public CommonResult generateMerRepayPlan(HttpServletRequest request, @RequestParam Map<String, String> params) {

        String merchantNo = WebUtil.getCurrMerchantUser().getMerchantNo();
        log.info("商户号[{}]生成计划参数[{}]", new Object[]{merchantNo, JsonUtil.objectToJson(params)});

        try {
            String oemType = params.get("oemType");
            String origRepaInfoCacheNo = params.get("origRepaInfoCacheNo");
            oemType = StringUtil.isBlank(oemType) ? Constants.DEFAULT_OEM_TYPE : oemType;

            Map<String, Object> repayInfoRedisObj = jedisClientSingle.getMap(origRepaInfoCacheNo);
            log.info("商户号[{}]建立还款计划redis获取缓存内容：" + JsonUtil.objectToJson(repayInfoRedisObj));
            if (null == repayInfoRedisObj || repayInfoRedisObj.isEmpty()) {
                return CommonResult.build(403, "建立计划失败，请重新再试");
            }

            String repayType = String.valueOf(repayInfoRedisObj.get("repayType"));
            String acqCode = String.valueOf(repayInfoRedisObj.get("acqCode"));
            String cardNo = String.valueOf(repayInfoRedisObj.get("cardNo"));
            String repayAmount = String.valueOf(repayInfoRedisObj.get("repayAmount"));
            String firstTradeAmount = String.valueOf(repayInfoRedisObj.get("firstTradeAmount"));
            String totalPayAmount = String.valueOf(repayInfoRedisObj.get("totalPayAmount"));
            String totalMerFee = String.valueOf(repayInfoRedisObj.get("totalMerFee"));
            String totalRepayNum = String.valueOf(repayInfoRedisObj.get("totalRepayNum"));
            String repayBeginTime = String.valueOf(repayInfoRedisObj.get("repayBeginTime"));
            String repayEndTime = String.valueOf(repayInfoRedisObj.get("repayEndTime"));
            String repayDesc = String.valueOf(repayInfoRedisObj.get("repayDesc"));

            String merFeeRate = String.valueOf(repayInfoRedisObj.get("merFeeRate"));
            String merSingleFee = String.valueOf(repayInfoRedisObj.get("merSingleFee"));

            //校验商户号
            Map<String, Object> merInfoMap = merchantService.getMerInfoByMerNo(merchantNo);
            if (merInfoMap == null || merInfoMap.isEmpty()) {
                return CommonResult.build(403, "商户编号不存在");
            }
            //校验卡片编号
            Map<String, Object> cardInfo = cardManageService.getCardManageByCardNo(cardNo);
            if (cardInfo == null || cardInfo.isEmpty()) {
                return CommonResult.build(403, "卡片编号不存在");
            }
            //校验通道信息
            Map<String, Object> routeChannel = WebUtil.getPayChannelInfo(acqCode);
            String acqMerchantNo = StringUtil.filterNull(routeChannel.get("pay_merchant_no"));
            String channelStatus = StringUtil.filterNull(routeChannel.get("channel_status"));
            if (!"1".equals(channelStatus)) {
                log.info(acqCode + "当前通道已关闭，不能建立计划");
                return CommonResult.build(403, "暂不支持此卡建立计划");
            }
            //获取代理商费率节点信息
            Map<String, String> agentNodeFeeMap = merchantService.getAgentNodeFeeByMerNo(merchantNo, repayType);
            if (null == agentNodeFeeMap || agentNodeFeeMap.isEmpty()) {
                return CommonResult.build(403, "代理商成本有误");
            }
            String agentFeeRate = agentNodeFeeMap.get("agentFeeRate");
            String agentSingleFee = agentNodeFeeMap.get("agentSingleFee");

            List<Map<String, String>> origRepaymentPlanList = (List<Map<String, String>>) repayInfoRedisObj.get("origRepayDetailListInfo");
            log.info(merchantNo + "/" + cardNo + "/" + "建立还款redis获取还款明细内容：" + JsonUtil.objectToJson(origRepaymentPlanList));
            if (CollectionUtils.isEmpty(origRepaymentPlanList)) {
                return CommonResult.build(403, "建立计划失败，请重新下单");
            }
            String payAmount = firstTradeAmount;
            String ensureAmount = firstTradeAmount;
            String repayNum = totalRepayNum;
            String repayFee = totalMerFee;
            //入库开始还款时间默认从当天开始
            repayBeginTime = DateUtil.getDate() + " 00:00:00";

            String batchNo = WebUtil.nextUniqueSeqNo("t_repay_plan", "", 20);
            Map<String, String> map = new HashMap<>();
            map.put("merchantNo", merchantNo);
            map.put("batchNo", batchNo);
            map.put("cardNo", cardNo);
            map.put("repayAmount", repayAmount);
            map.put("repayBeginTime", repayBeginTime);
            map.put("repayEndTime", repayEndTime);
            map.put("ensureAmount", ensureAmount);
            map.put("repayFee", repayFee);
            map.put("repayNum", repayNum);
            map.put("merFeeRate", merFeeRate);
            map.put("merSingleFee", merSingleFee);
            map.put("agentFeeRate", agentFeeRate);
            map.put("agentSingleFee", agentSingleFee);
            map.put("repayFee", repayFee);
            map.put("acqCode", acqCode);
            map.put("acqMerchantNo", acqMerchantNo);
            map.put("repayType", repayType);
            map.put("repayDesc", repayDesc);

            origRepaymentPlanList = repayPlanService.generateRepayPlan(repayType, map, origRepaymentPlanList);
            if (origRepaymentPlanList == null) {
                return CommonResult.build(403, "新增还款计划失败");
            }
            //重复提交校验
            DuplicateSubmitCheck duplicateSubmitCheck = new DuplicateSubmitCheck();
            String duplicatePayTimeStamp = duplicateSubmitCheck.setDuplicateTime(request.getSession());
            //返回参数集合
            Map<String, Object> resMap = new HashMap<>();

            resMap.put("duplicatePayTimeStamp", duplicatePayTimeStamp);
            resMap.put("batchNo", batchNo);
            resMap.put("payAmount", payAmount);
            resMap.put("payCardInfo", cardInfo);

            resMap.put("isNeedSafeInput", "1");
            return CommonResult.ok(resMap);

        } catch (Exception e) {
            log.error("商户号[{}]还款计划异常[{}]", new Object[]{merchantNo, e});
            e.printStackTrace();
            return CommonResult.build(403, "操作失败，请稍候再试");
        }
    }


    /**
     * 终止还款计划
     *
     * @param params
     * @return
     */
    @MerAuthStatusCheck
    @RequestMapping(value = "/stopRepayPlan", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
    @ResponseBody
    public CommonResult stopRepayPlan(@RequestParam Map<String, String> params) {

        String merchantNo = WebUtil.getCurrMerchantUser().getMerchantNo();
        log.info("商户号[{}]终止还款计划参数[{}]", new Object[]{merchantNo, JsonUtil.objectToJson(params)});

        String batchNo = params.get("batchNo");
        try {
            if (StringUtils.isBlank(batchNo)) {
                return CommonResult.build(403, "终止的计划编号不能为空");
            }
            CommonResult commonResult = repayPlanService.stopRepayPlan(batchNo);
            return commonResult;

        } catch (Exception e) {
            log.error("商户号[{}]终止还款计划[{}]异常[{}]", new Object[]{merchantNo, batchNo, e});
            e.printStackTrace();
            return CommonResult.build(403, "操作失败，请稍候再试");
        }
    }


    /**
     * 付款
     *
     * @param params
     * @return
     */
    @MerAuthStatusCheck
    @RequestMapping(value = "/payFirst", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
    @ResponseBody
    public CommonResult payFirst(HttpServletRequest request, @RequestParam Map<String, String> params) {

        String merchantNo = WebUtil.getCurrMerchantUser().getMerchantNo();
        log.info("商户号[{}]确认付款接口参数[{}]", new Object[]{merchantNo, JsonUtil.objectToJson(params)});

        String batchNo = params.get("batchNo");
        String cardNo = params.get("cardNo");
        String payAmount = params.get("payAmount");
        String cvn2 = params.get("cvn2");
        String expDate = params.get("expDate");
        String isNeedSafeInput = params.get("isNeedSafeInput");
        String duplicatePayTimeStamp = params.get("duplicatePayTimeStamp");
        try {

            if (StringUtil.isBlank(batchNo, cardNo, payAmount, duplicatePayTimeStamp)) {
                return CommonResult.build(403, "必要参数不能有空值");
            }
            if ("1".equals(isNeedSafeInput)) {
                if (StringUtil.isBlank(cvn2, expDate)) {
                    return CommonResult.build(403, "必要参数不能有空值");
                }
                if (cvn2.length() != 3) {
                    return CommonResult.build(403, "CVV2格式不正确");
                }
                if (expDate.length() != 4) {
                    return CommonResult.build(403, "有效期格式不正确");
                }
            }

            //校验是否重复提交
            DuplicateSubmitCheck duplicateSubmitCheck = new DuplicateSubmitCheck();
            Boolean checkResult = duplicateSubmitCheck.verifyDuplicate(request.getSession(), duplicatePayTimeStamp);
            if (!checkResult) {
                return CommonResult.build(403, "请勿重复提交");
            }

            //校验卡信息
            Map<String, Object> cardInfoMap = cardManageService.getCardManageByCardNo(cardNo);
            log.info("商户编号[{}]确认付款卡片编号号[{}]对应的卡片信息[{}]", new Object[]{merchantNo, cardNo, JsonUtil.objectToJson(cardInfoMap)});
            if (cardInfoMap == null || cardInfoMap.isEmpty()) {
                return CommonResult.build(403, "付款卡不存在");
            }

            //校验计划批次号
            RepayPlan repayPlan = repayPlanService.getRepayPlanByBatchNo(batchNo);
            log.info("商户编号[{}]确认付款计划批次号[{}]对应的计划信息[{}]", new Object[]{merchantNo, batchNo, JsonUtil.objectToJson(repayPlan)});
            if (repayPlan == null) {
                return CommonResult.build(403, "还款计划不存在");
            }
            String planCardNo = repayPlan.getCardNo();

            if (!cardNo.equals(planCardNo)) {
                return CommonResult.build(403, "只能使用本卡支付");
            }

            //校验付款金额
            RepayPlanDetail planDetail = planDetailService.getFirstDetail(batchNo);
            log.info("商户编号[{}]确认付款计划批次号[{}]对应的第一笔明细信息[{}]", new Object[]{merchantNo, batchNo, JsonUtil.objectToJson(planDetail)});
            if (planDetail == null) {
                return CommonResult.build(403, "还款计划不合法");
            }
            if (!Constants.PLAN_DETAIL_TYPE_OUT.equals(planDetail.getPlanType())) {
                return CommonResult.build(403, "生成的明细不合法，不能支付");
            }
            if (Integer.parseInt(payAmount) - planDetail.getPlanAmount().intValue() != 0) {
                return CommonResult.build(403, "付款金额不对");
            }

            //校验通道信息
            String acqCode = repayPlan.getAcqCode();
            Map<String, Object> channelMap = WebUtil.getPayChannelInfo(acqCode);
            log.info("商户编号[{}]确认付款计划批次号[{}]对应通道编码[{}]的通道信息[{}]", new Object[]{merchantNo, batchNo, acqCode, JsonUtil.objectToJson(planDetail)});
            if (channelMap.isEmpty()) {
                return CommonResult.build(403, "支付失败，请稍候再试");
            }

            params.put("merchantNo", merchantNo);

            //根据通道编码获取对应支付渠道服务类
            QuickPayService quickPayService = WebUtil.getQuickPayServiceByAcqCode(repayPlan.getAcqCode());
            log.info("商户编号[{}]确认付款计划批次号[{}]对应的通道服务类[{}]", new Object[]{merchantNo, batchNo, quickPayService});
            if (quickPayService == null) {
                return CommonResult.build(403, "还款计划不合法");
            }

            return quickPayService.payFirst(params);

        } catch (Exception e) {
            e.printStackTrace();
            log.error("商户号[{}]还款计划[{}]确认付款异常[{}]", new Object[]{merchantNo, batchNo, e});
            return CommonResult.build(403, "操作失败，请稍候再试");
        }
    }


    /**
     * 确认付款（用户下发短信验证码操作的）
     *
     * @param params
     * @return
     */
    @MerAuthStatusCheck
    @RequestMapping(value = "/confirmPayFirst", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
    @ResponseBody
    public CommonResult confirmPayFirst(HttpServletRequest request, @RequestParam Map<String, String> params) {

        String merchantNo = WebUtil.getCurrMerchantUser().getMerchantNo();
        log.info("商户号[{}]确认付款接口参数[{}]", new Object[]{merchantNo, JsonUtil.objectToJson(params)});

        String batchNo = params.get("batchNo");
        String cardNo = params.get("cardNo");
        String payAmount = params.get("payAmount");
        String cvn2Des3 = params.get("cvn2");
        String expDateDes3 = params.get("expDate");
        String service = params.get("service");
        String serviceOrderNo = params.get("serviceOrderNo");
        String serviceValue = params.get("serviceValue");
        try {

            if (StringUtil.isBlank(batchNo, cardNo, payAmount, cvn2Des3, expDateDes3, service, serviceOrderNo, serviceValue)) {
                return CommonResult.build(403, "必要参数不能有空值");
            }
            //校验卡信息
            Map<String, Object> cardInfoMap = cardManageService.getCardManageByCardNo(cardNo);
            log.info("商户编号[{}]确认付款卡片编号号[{}]对应的卡片信息[{}]", new Object[]{merchantNo, cardNo, JsonUtil.objectToJson(cardInfoMap)});
            if (cardInfoMap == null || cardInfoMap.isEmpty()) {
                return CommonResult.build(403, "付款卡不存在");
            }

            //校验计划批次号
            RepayPlan repayPlan = repayPlanService.getRepayPlanByBatchNo(batchNo);
            log.info("商户编号[{}]确认付款计划批次号[{}]对应的计划信息[{}]", new Object[]{merchantNo, batchNo, JsonUtil.objectToJson(repayPlan)});
            if (repayPlan == null) {
                return CommonResult.build(403, "还款计划不存在");
            }
            String planCardNo = repayPlan.getCardNo();

            if (!cardNo.equals(planCardNo)) {
                return CommonResult.build(403, "只能使用本卡支付");
            }

            //校验付款金额
            RepayPlanDetail planDetail = planDetailService.getFirstDetail(batchNo);
            log.info("商户编号[{}]确认付款计划批次号[{}]对应的第一笔明细信息[{}]", new Object[]{merchantNo, batchNo, JsonUtil.objectToJson(planDetail)});
            if (planDetail == null) {
                return CommonResult.build(403, "还款计划不合法");
            }
            if (!Constants.PLAN_DETAIL_TYPE_OUT.equals(planDetail.getPlanType())) {
                return CommonResult.build(403, "生成的明细不合法，不能支付");
            }
            if (Integer.parseInt(payAmount) - planDetail.getPlanAmount().intValue() != 0) {
                return CommonResult.build(403, "付款金额不对");
            }

            //校验通道信息
            String acqCode = repayPlan.getAcqCode();
            Map<String, Object> channelMap = WebUtil.getPayChannelInfo(acqCode);
            log.info("商户编号[{}]确认付款计划批次号[{}]对应通道编码[{}]的通道信息[{}]", new Object[]{merchantNo, batchNo, acqCode, JsonUtil.objectToJson(planDetail)});
            if (channelMap.isEmpty()) {
                return CommonResult.build(403, "支付失败，请稍候再试");
            }

            //根据通道编码获取对应支付渠道服务类
            QuickPayService quickPayService = WebUtil.getQuickPayServiceByAcqCode(repayPlan.getAcqCode());
            log.info("商户编号[{}]确认付款计划批次号[{}]对应的通道服务类[{}]", new Object[]{merchantNo, batchNo, quickPayService});
            if (quickPayService == null) {
                return CommonResult.build(403, "还款计划不合法");
            }

            params.put("cvn2", DES3Coder.decryptToString(cvn2Des3));
            params.put("expDate", DES3Coder.decryptToString(expDateDes3));
            params.put("merchantNo", merchantNo);
            return quickPayService.confirmPayFirst(params);

        } catch (Exception e) {
            e.printStackTrace();
            log.error("商户号[{}]还款计划[{}]确认付款异常[{}]", new Object[]{merchantNo, batchNo, e});
            return CommonResult.build(403, "操作失败，请稍候再试");
        }
    }


    /**
     * 激活还款计划（由挂起到还款中）
     *
     * @return
     */
    @RequestMapping(value = "/wakeRepayPlan", produces = MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
    @ResponseBody
    public CommonResult wakeRepayPlan(HttpServletRequest request, @RequestParam Map<String, String> params) {

        String batchNos = request.getParameter("batchNos");
        String operator = request.getParameter("operator");
        if (StringUtil.isEmpty(batchNos)) {
            return CommonResult.build(403, "计划批次号不能为空");
        }
        if (StringUtil.isEmpty(operator)) {
            return CommonResult.build(403, "操作人不能为空");
        }
        String[] batchNoArray = batchNos.split(",");
        if (null == batchNoArray || batchNoArray.length < 1) {
            return CommonResult.build(403, "计划批次号不能为空");
        }

        for (String batchNo : batchNoArray) {
            try {
                repayPlanService.wakeRepayPlan(batchNo, operator);
            } catch (Exception e) {
                log.error("还款计划批次号[{}]激活异常[{}]", new Object[]{batchNo, operator});
                e.printStackTrace();
            }
        }
        return CommonResult.ok();
    }


    /**
     * 根据账单日和还款日获取信用卡的还款周期
     *
     * @param statementDateStr
     * @param repaymentDateStr
     * @return
     */
    private CommonResult getCreditRepayTime(String statementDateStr, String repaymentDateStr) throws Exception {

        if (StringUtil.isBlank(statementDateStr, repaymentDateStr)) {
            return CommonResult.build(403, "信用卡账单日或还款日不能为空");
        }
        int statementDay = 0;
        int repaymentDay = 0;
        try {
            statementDay = Integer.parseInt(statementDateStr);
            repaymentDay = Integer.parseInt(repaymentDateStr);
        } catch (Exception e) {
            return CommonResult.build(403, "信用卡账单日或还款日设置不合法");
        }

        String repayBeginTime = "";
        String repayEndTime = "";

        String currYearAndMonth = DateUtil.getDate("yyyy-MM");
        String currDayStr = DateUtil.getDay();
        int currDay = Integer.parseInt(currDayStr);

        //如果还款日大于账单日，表示在同一个月份，否则表示跨月了
        if (repaymentDay > statementDay) {

            if (currDay < statementDay || currDay >= repaymentDay) {
                return CommonResult.build(403, "只能在账单日和还款日之间建立计划");
            }

            repayBeginTime = DateUtil.getDate("yyyy-MM-dd");
            repayEndTime = currYearAndMonth + "-" + (repaymentDay < 10 ? ("0" + repaymentDay) : repaymentDay);
        } else {

            if (currDay < statementDay && currDay >= repaymentDay) {
                return CommonResult.build(403, "只能在账单日和还款日之间建立计划");
            }

            //如果大于账单日表示还款结束日期跨月，否则，不跨月
            if (currDay >= statementDay) {

                repayBeginTime = DateUtil.getDate("yyyy-MM-dd");
                currYearAndMonth = DateUtil.formatDate(DateUtil.addMonths(new Date(), 1), "yyyy-MM");
                repayEndTime = currYearAndMonth + "-" + (repaymentDay < 10 ? ("0" + repaymentDay) : repaymentDay);
            }
            if (currDay < repaymentDay) {

                repayBeginTime = DateUtil.getDate("yyyy-MM-dd");
                repayEndTime = currYearAndMonth + "-" + (repaymentDay < 10 ? ("0" + repaymentDay) : repaymentDay);
            }
        }
        Map<String, String> resMap = new HashMap<>();
        resMap.put("repayBeginTime", repayBeginTime);
        resMap.put("repayEndTime", repayEndTime);
        return CommonResult.ok(resMap);
    }
}
